iT邦幫忙

2021 iThome 鐵人賽

DAY 21
0
Modern Web

不只是串串API,新手前端30天系列 第 21

DAY21 - 網頁可以操作電腦裡的檔案?!本地端檔案覆寫 - The File System Access API

  • 分享至 

  • xImage
  •  

其實也是某天收到這個需求,一開始覺得怎麼可能,網頁要存我電腦的檔案!什麼情境才會用到這個功能...還真的有,當你的網頁是一個編輯工具,儲存可以存到自己的電腦,也可以把電腦的檔案開啟在編輯器編輯,編一編之後又可以回存回去~ 是不是!很親民的需求XD

今天我們要來介紹的就是可以幫助我們在瀏覽器上存取本機檔案的The File System Access API

The File System Access API

前身其實稱作Native File System API,更早之前也稱作Writeable Files API。The File System Access可以讓web可以直接存取、修改使用者裝置裡面的檔案。

應用場景

通常是會用在開發在web的編輯器時,EX:圖片編輯器,在線上調整圖檔顏色、濾鏡等等,存到裝置後,又想在對圖檔修改,再回存。文字編輯器的範例應用可以參考這個連結

瀏覽器支援度

目前支援度其實蠻少的只支援chrome, edge, opera

重點物件介紹

檔案物件:FileSystemFileHandle

透過window.showOpenFilePicker選取檔案後取得的物件

  • getFile: 從FileSystemFileHandle取得file物件
  • createWritable: 要讓檔案物件成為可寫入需呼叫
    //writable可透過write覆寫內容至檔案,寫完之後呼叫close關閉writable狀態
    writable.write(contents);
    writable.close();
    

方法:showOpenFilePicker 選擇檔案

打開file manager,讓使用者選取檔案

方法:showSaveFilePicker 儲存檔案

打開file manager,讓使用者選取位置後,儲存檔案

功能介紹

以下程式皆以vue.js為例

應用01. showOpenFilePicker打開file manager讓使用者選取檔案

注意,這邊是「不需透過input type="file"」,可以再任意的元素上ex.button上開啟file manager

UI畫面

  • 點選按鈕
  • 開啟file manager

程式

<div class="section-save">
    <p>模擬1: 開啟檔案</p>
    <button @click="openFileNew">Open</button>
</div>
async openFileNew(){
  let fileHandle;
  [fileHandle] = await window.showOpenFilePicker();
  //FileSystemFileHandle物件
  const file = await fileHandle.getFile(); // file物件
  console.log('file',file);
},
  1. 透過showOpenFilePicke打開file manger,選取的檔案為FileSysteFileHandle物件
  2. FileSysteFileHandle物件透過getFile()就可取得當中的file物件,便可使用之前file物件的處理方式

應用02. showSaveFilePicker writeFile 選定位置,另存新檔後,修改後可直接存至原檔

UI畫面

  • 點選save to local按鈕
  • 透過showSaveFilePicker打開save file的視窗
  • 按儲存後會再選擇的位置存擋
  • 修改輸入框的文字內容,會直接回存到剛剛的example.zip

程式

<div class="section-save">
    <p>模擬2: 第一次存新檔案,後續存回原檔</p>
    <input type="text" v-model="textContent">
    <button @click="saveNew">save to local</button>
</div>
/***
 * 存檔
 ***/
async saveNew(){
    const _self = this;
    console.log('save new', this.saveNew);
    let zip = new JSZip(); // 建立jszip物件 準備打包
    zip.file('test.txt', this.textContent); //將文件檔案放到zip中
    //第一次存檔 選擇位置存zip
    if(_self.isSaveFirstTime){
        let options = {
            suggestedName: 'example.zip', //給予存檔預設名字
            types:[{
                accept: {
                  'application/zip': ['.zip'], //建議zip格式
                },
            }]
        }
        this.targetFileObject = await window.showSaveFilePicker(options); //儲存時
        console.log('handle',this.targetFileObject); //FileSystemFileHandle
        _self.isSaveFirstTime = false;
    }

    zip.generateAsync({type: 'blob'}).then(async (content)=>{
        // 寫進檔案中
        await _self.writeFile(this.targetFileObject, content)
        _self.isSaveFirstTime = false;
    })


},
/***寫入本地端檔案 
 * fileHandle: 需為FileSystemFileHandle物件
 * contents: 要被寫入的內容
 ***/
async writeFile(fileHandle, contents) {
console.log("===== Write File Start =====");
//如果已經建立過createWritable,則直接覆寫
if (fileHandle.createWriter) {
    const writer = await fileHandle.createWriter();  // createWriter
    await writer.write(0, contents);
    await writer.close();
    return;
}
//為建立過createWritable,建立後覆寫
const writable = await fileHandle.createWritable(); // createWritable
await writable.write(contents);
await writable.close();
console.log("===== Write File End =====");
},
  • jsZip和saveAs的用法可以參考昨天的文章喔XD

附上實際的程式檔案,給大家玩玩 [連結]


常見錯誤分享

在專案開發時,寫到存檔至地端時曾經發生以下錯誤:

failed to execute 'showSaveFilePicker' on 'Window': Must be handling a user gesture to show a file picker.

錯誤原因

這個錯誤是因為,有的時候要被儲存的檔案處理時間比較久,還正在處理的同時我們也呼叫的showSaveFilePicker的視窗,才會有這個錯誤。

解決辦法

建議順序可以調整成:先把存擋的位置處理好後,再去做檔案相關的處理。就確保不會有檔案還在處理又要乎呼叫showSaveFilePicker的狀況。
意即 showSaveFilePicker選完儲存位置 -> 檔案資料處理


嘮叨廢話time可跳過~~~
終於~~~ 檔案處理應該是最後一天分享了,
可能還有很多需要改進跟講得更清楚,不過因為還有別的主題還想寫,
就先把我自己很基礎的認知跟覺得很常見的應用,在這幾天介紹給他~~
明天工作上有重要的事等等要去努力一下,今天就到這邊囉,希望明天順利RRR


Ref


上一篇
DAY20 - 檔案處理 - 利用jszip和file-saver,製作網頁下載zip檔案
下一篇
DAY22 - 利用開發者工具列Network import export debug
系列文
不只是串串API,新手前端30天30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言